package com.huang.security;

import cn.hutool.core.util.ArrayUtil;
import com.huang.constant.Common;
import com.huang.security.handler.DefaultAccessDeniedHandler;
import com.huang.security.handler.DefaultAuthenticationEntryPoint;
import com.huang.security.handler.DefaultAuthorizationManager;
import com.huang.security.handler.IgnoreUrlsRemoveJwtFilter;
import com.huang.security.properties.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.List;

/**
 * security 核心配置
 * @author han
 * @since 2022-01-16 14:01:28
 */
@Configuration
@EnableWebFluxSecurity // 开启security配置
public class WebfluxSecurityConfig {

    @Resource
    private SecurityProperties securityProperties;
    @Resource
    private DefaultAccessDeniedHandler defaultAccessDeniedHandler;
    @Resource
    private DefaultAuthorizationManager defaultAuthorizationManager;
    @Resource
    private DefaultAuthenticationEntryPoint defaultAuthenticationEntryPoint;
    @Resource
    private IgnoreUrlsRemoveJwtFilter ignoreUrlsRemoveJwtFilter;


    @Bean
    public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix(Common.Authority.PREFIX);
        jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(Common.Authority.JWT_AUTHORITIES_KEY);

        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
    }

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity serverHttpSecurity) {
        serverHttpSecurity.oauth2ResourceServer()
                // jwt 增加
                .jwt().jwtAuthenticationConverter(jwtAuthenticationConverter());
                // .jwkSetUri(""); 远程访问地址，已经在文件中配置
        // 自定义处理JWT请求头过期或签名错误的结果
        serverHttpSecurity.oauth2ResourceServer()
                .accessDeniedHandler(defaultAccessDeniedHandler) // 未授权
                .authenticationEntryPoint(defaultAuthenticationEntryPoint); // 自定义未认证、token过期

        // 获取报名单
        List<String> whiteList = securityProperties.getWhiteList();
        //对白名单路径，直接移除JWT请求头
        serverHttpSecurity.addFilterBefore(ignoreUrlsRemoveJwtFilter, SecurityWebFiltersOrder.AUTHENTICATION);

        serverHttpSecurity.httpBasic().disable()
                .authorizeExchange()
                .pathMatchers(ArrayUtil.toArray(whiteList, String.class)).permitAll()
                .pathMatchers(HttpMethod.OPTIONS).permitAll()
                // 自定义鉴权
                .anyExchange().access(defaultAuthorizationManager)
                .and()
                .exceptionHandling()
                .accessDeniedHandler(defaultAccessDeniedHandler) // 处理未授权
                .authenticationEntryPoint(defaultAuthenticationEntryPoint) //处理未认证
                .and()
                .csrf()
                .disable();
        return serverHttpSecurity.build();
    }


    /**
     * BCrypt密码编码
     */
    @Bean("passwordEncoder")
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

}
